

package top.continew.admin.question.manger.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.common.context.UserContextHolder;
import top.continew.admin.question.config.QuestionEvent;
import top.continew.admin.question.config.QuestionEventSender;
import top.continew.admin.question.manger.base.PageFactory;
import top.continew.admin.question.manger.service.model.*;
import top.continew.admin.question.mapper.question.QuestionKnowledgePointMapper;
import top.continew.admin.question.mapper.question.QuestionMapper;
import top.continew.admin.question.model.question.Question;
import top.continew.admin.question.model.question.QuestionSelect;
import top.continew.admin.question.manger.service.PointService;
import top.continew.admin.question.manger.service.MangerQuestionService;
import top.continew.admin.question.manger.service.msg.MessageBean;
import top.continew.admin.question.manger.service.msg.MessageTopic;
import top.continew.admin.question.manger.service.msg.MessageType;
import top.continew.admin.question.user.service.question.enums.QuestionTypeEnum;
import top.continew.starter.core.validation.ValidationUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Service
public class MangerQuestionServiceImpl implements MangerQuestionService {

    private QuestionMapper questionMapper;

    private QuestionKnowledgePointMapper pointMapper;

    private PointService pointService;

    @Autowired
    private QuestionEventSender questionEventSender;

    public MangerQuestionServiceImpl(QuestionMapper questionMapper,
                                     QuestionKnowledgePointMapper pointMapper,
                                     PointService pointService) {
        this.questionMapper = questionMapper;
        this.pointMapper = pointMapper;
        this.pointService = pointService;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SaveQuestion save(SaveQuestion saveQuestion) {
        List<PointVO> pointVOS = new ArrayList<>();
        if (saveQuestion.getId() != null) {
            QuestionInfo old = getInfo(saveQuestion.getId());
            SaveQuestion olds = new SaveQuestion();
            BeanUtils.copyProperties(old, olds);
            // 查询知识点
            pointVOS = pointService.getQuestionPointList(saveQuestion.getId());
            olds.setKnowledgePoints(pointVOS);
        }
        Question question = new Question();
        BeanUtils.copyProperties(saveQuestion, question);
        if (question.getParentId() == null) {
            question.setParentId(0L);
        }
        if (saveQuestion.getIsAi() == null) {
            saveQuestion.setIsAi(2L);
        }
        question.setSpecificationType(saveQuestion.getSubjectId());
        question.setCtime(DateUtil.date().getTime());
        question.setUtime(DateUtil.date().getTime());
        question.setOnlineStatus(saveQuestion.getOnlineStatus());
        question.insertOrUpdate();
        // 添加选项
        if (saveQuestion.getSelect() != null) {
            new QuestionSelect().delete(new QueryWrapper<QuestionSelect>().eq("question_id", question.getId()));
            Question finalQuestion = question;
            saveQuestion.getSelect().forEach(select -> {
                QuestionSelect questionSelect = new QuestionSelect();
                questionSelect.setQuestionId(finalQuestion.getId());
                questionSelect.setContent(select.getContent());
                questionSelect.setSelectNo(select.getSelect());
                questionSelect.insert();
            });
        }
        saveQuestion.setId(question.getId());
        // 删除知识点
        pointVOS.forEach(res -> {
            if (saveQuestion.getKnowledgePoints().stream().noneMatch(point -> res.getId().equals(point.getId()))) {
                pointMapper.deleteById(res.getId());
            }
        });
        // 添加知识点
        AtomicLong index = new AtomicLong(1);
        if (saveQuestion.getKnowledgePoints() != null && !saveQuestion.getKnowledgePoints().isEmpty()) {
            Question finalQuestion1 = question;
            saveQuestion.getKnowledgePoints().forEach(res -> {
                res.setSort(index.get());
                index.getAndIncrement();
                if (res.getId() == null) {
                    pointService.addQuestionCorrelationPoint(finalQuestion1.getId(), res.getPointId(), res.getName());
                } else {
                    pointService.pointSort(String.valueOf(finalQuestion1.getId()), String.valueOf(res.getPointId()), res
                        .getSort());
                }
            });
            // 查询知识点
            saveQuestion.setKnowledgePoints(pointService.getQuestionPointList(saveQuestion.getId()));
        }
        question = new Question().selectById(question.getId());
        if (saveQuestion.getKnowledgePoints() != null) {
            questionEventSender.sendEvent(QuestionEvent.builder()
                .questionOnlineStatus(String.valueOf(question.getOnlineStatus() == null
                    ? 2
                    : question.getOnlineStatus()))
                .questionId(question.getId())
                .action("add")
                .knowledgeIds(saveQuestion.getKnowledgePoints()
                    .stream()
                    .map(PointVO::getPointId)
                    .collect(Collectors.toList()))
                .build());
        }
        return saveQuestion;
    }

    @Override
    public List<QuestionVO> getList(QueryQuestion queryQuestion) {
        return PageFactory.page(() -> questionMapper.getList(queryQuestion, UserContextHolder.getUserId()));
    }

    @Override
    public QuestionInfo getInfo(Long id) {
        QuestionInfo questionInfo = new QuestionInfo();
        Question question = new Question().selectById(id);
        ValidationUtils.throwIfEmpty(question, "题目没有找到~");
        BeanUtils.copyProperties(question, questionInfo);
        questionInfo.setSubjectId(question.getSpecificationType());
        List<QuestionSelect> questionSelects = new QuestionSelect().selectList(new QueryWrapper<QuestionSelect>()
            .eq("question_id", question.getId()));
        questionInfo.setSelect(questionSelects.stream().map(s -> {
            Select select = new Select();
            select.setContent(s.getContent());
            select.setSelect(s.getSelectNo());
            return select;
        }).collect(Collectors.toList()));
        questionInfo.setReadonly(false);
        return questionInfo;
    }

    /**
     * 当前用户是否可编辑
     */
    private boolean questionIsReadOnly(Question question) {
        boolean flag;
        if (question.getStatus().intValue() == 0) {
            flag = !Objects.equals(UserContextHolder.getUserId(), question.getEditPeople());
        } else {
            flag = true;
        }
        return flag;
    }

    private void hasCommitPermission(Question question) {
        if (question.getStatus().intValue() == 0) {
            ValidationUtils.throwIf(question.getEditPeople().equals(UserContextHolder.getUserId()), "您没有权限提交该题的校验!");
        }
    }

    @Override
    public void commitCheck(Long questionId, Long checkPeople) {
        Question question = new Question().selectById(questionId);
        ValidationUtils.throwIfEmpty(question, "题目不存在!");
        ValidationUtils.throwIfEmpty(checkPeople, "老师不能为空!");
        checkQuestion(question);
        hasCommitPermission(question);
        question.setEditStatus(1L);
        question.setStatus(3L);
        question.updateById();
    }

    /**
     * 校验题目
     */
    private void checkQuestion(Question question) {
        ValidationUtils.throwIfEmpty(question.getTitle(), "标题不能为空!");
        if (!(question.getType() == 4 || question.getType() == 8 || question.getType() == 9 || question
            .getType() == 10)) {
            ValidationUtils.throwIfEmpty(question.getAnswer(), "答案不能为空!");
            ValidationUtils.throwIfEmpty(question.getAnswer().replaceAll("\\[\\]", ""), "答案不能为空!");
            ValidationUtils.throwIfEmpty(question.getAnalyze(), "解析不能为空!");
            List<PointVO> pointVOS = pointService.getQuestionPointList(question.getId());
            ValidationUtils.throwIf(pointVOS.size() > 0, "知识点不能为空!");
        }
    }

    @Override
    public void rejectCheck(Long questionId, String msg) {
        Question question = new Question().selectById(questionId);
        ValidationUtils.throwIfEmpty(question, "题目不存在!");
        hasCommitPermission(question);
        question.setStatus(0L);
        question.setEditStatus(2L);
        question.updateById();
        MessageBean.builder()
            .topic(MessageTopic.QUESTION_VERIFICATION_REJECTION)
            .message("您好,您提交的题目ID为:" + question.getId() + "的题目校验被驳回,驳回理由:" + msg)
            .type(MessageType.ERROR)
            .url("{\"path\":\"/questionInfo\",\"query\":{\"id\":" + questionId + "}}")
            .build()
            .addCustomer(question.getEditPeople(), 2)
            .send();
    }

    @Override
    public void rejectCheckMultiple(List<Long> questionIds, String msg) {
        ValidationUtils.throwIf(questionIds != null && questionIds.size() > 0, "驳回数量必须大于0!");
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getStatus, 3L);
        queryWrapper.eq(Question::getEditStatus, 1L);
        queryWrapper.in(Question::getId, questionIds);
        List<Question> questions = this.questionMapper.selectList(queryWrapper);
        List<Long> updateIds = questions.stream().map(Question::getId).collect(Collectors.toList());
        // 可以驳回的id为空时，直接跳过
        ValidationUtils.throwIf(updateIds.size() > 0, "通过状态筛选后无可驳回题目!");

        // 设置驳回
        this.questionMapper.rejectCheckMultiple(updateIds, System.currentTimeMillis());

        // 发送消息
        for (Question q : questions) {
            MessageBean.builder()
                .topic(MessageTopic.QUESTION_VERIFICATION_REJECTION)
                .message("您好,您提交的题目ID为:" + q.getId() + "的题目校验被驳回,驳回理由:" + (msg != null && !msg.equals("") ? msg : "无"))
                .type(MessageType.ERROR)
                .url("{\"path\":\"/questionInfo\",\"query\":{\"id\":" + q.getId() + "}}")
                .build()
                .addCustomer(q.getEditPeople(), 2)
                .send();
        }
    }

    @Override
    public void commitReview(Long questionId, Long reviewPeople) {
        Question question = new Question().selectById(questionId);
        ValidationUtils.throwIfEmpty(question, "题目不存在!");
        ValidationUtils.throwIfEmpty(reviewPeople, "老师不能为空!");
        checkQuestion(question);
        hasCommitPermission(question);
        question.setStatus(4L);
        question.updateById();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rejectReview(Long questionId, String msg) {
        Question question = new Question().selectById(questionId);
        ValidationUtils.throwIfEmpty(question, "题目不存在!");
        hasCommitPermission(question);
        question.setStatus(3L);
        question.updateById();
    }

    @Override
    public void rejectReviewMultiple(String questionIds, String msg) {
        String[] ids = questionIds.split(",");
        ValidationUtils.throwIf(ids.length > 0, "驳回数量必须大于0!");
        // 获取可以驳回的题目
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getStatus, 5L);
        queryWrapper.in(Question::getId, ids);
        List<Question> questions = this.questionMapper.selectList(queryWrapper);
        List<Long> updateIds = questions.stream().map(Question::getId).collect(Collectors.toList());

        // 可以驳回的id为空时，直接跳过
        ValidationUtils.throwIf(updateIds.size() > 0, "通过状态筛选后无可驳回题目!");

        // 设置驳回
        this.questionMapper.rejectReviewMultiple(updateIds, System.currentTimeMillis());
    }

    @Override
    public void review(Long questionId) {
        Question question = new Question().selectById(questionId);
        ValidationUtils.throwIfEmpty(question, "题目不存在!");
        hasCommitPermission(question);
        question.setStatus(5L);
        question.updateById();
    }

    @Override
    public void questionSort(Long questionId, Long sort) {
        Question question = new Question().selectById(questionId);
        ValidationUtils.throwIfEmpty(question, "题目不存在!");
        question.setSort(sort);
        question.updateById();
    }

    @Override
    public void delete(String questionId) {
        Question question = new Question().selectById(questionId);
        ValidationUtils.throwIfEmpty(question, "题目不存在!");
        question.deleteById();
        questionEventSender.sendEvent(QuestionEvent.builder()
            .questionOnlineStatus(String.valueOf(question.getOnlineStatus()))
            .questionId(question.getId())
            .action("delete")
            .build());
    }

    @Override
    public List<TestStructureQuestionVO> getQuestionByQuestionSet(long questionSetId) {
        return questionMapper.getQuestionByQuestionSet(questionSetId);
    }

    @Override
    public List<QuestionVO> getListQuestionManager(QuestionQuery query) {
        query.strToDate();
        return PageFactory.page(() -> questionMapper.getListQuestionManager(query));
    }

    @Override
    public List<StatisticalTeacherVo> statisticalTeacher(String startTime, String endTime, Long teacherId) {
        Long start = null, end = null;
        try {
            start = Objects.requireNonNull(DateUtil.parse(startTime, "yyyy-MM-dd HH:mm:ss")).getTime();
            end = Objects.requireNonNull(DateUtil.parse(endTime, "yyyy-MM-dd HH:mm:ss")).getTime();
        } catch (NullPointerException e) {
            start = 0L;
            end = 0L;
        }
        Long finalStart = start;
        Long finalEnd = end;
        // 获取老师信息分页
        List<StatisticalTeacherVo> list = PageFactory.page(() -> questionMapper
            .statisticalTeacherLeft(finalStart, finalEnd, teacherId));
        List<Long> ids = list.stream().map(StatisticalTeacherVo::getId).collect(Collectors.toList());
        // 获取录题统计信息
        List<StatisticalTeacherInfoVo> infoVos = this.questionMapper.statisticalTeacherRight(ids, start, end);
        list.forEach(statisticalTeacherVo -> {
            // 根据老师id筛选数据，并根据学科分组
            Map<String, List<StatisticalTeacherInfoVo>> listMap = infoVos.stream()
                .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getId()
                    .equals(statisticalTeacherVo.getId()))
                .collect(Collectors.groupingBy(StatisticalTeacherInfoVo::getSubject));
            List<StatisticalTeacherSubjectVo> teacherSubjectVos = new ArrayList<>();
            // 统计学科录题信息
            for (Map.Entry<String, List<StatisticalTeacherInfoVo>> entry : listMap.entrySet()) {
                StatisticalTeacherSubjectVo statisticalTeacherSubjectVo = new StatisticalTeacherSubjectVo();
                statisticalTeacherSubjectVo.setSubject(entry.getKey());
                statisticalTeacherSubjectVo.setCheckNum(entry.getValue()
                    .stream()
                    .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getType().equals("check"))
                    .mapToLong(StatisticalTeacherInfoVo::getNum)
                    .sum());
                statisticalTeacherSubjectVo.setEditorNum(entry.getValue()
                    .stream()
                    .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getType().equals("edit"))
                    .mapToLong(StatisticalTeacherInfoVo::getNum)
                    .sum());
                statisticalTeacherSubjectVo.setReviewNum(entry.getValue()
                    .stream()
                    .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getType().equals("review"))
                    .mapToLong(StatisticalTeacherInfoVo::getNum)
                    .sum());
                teacherSubjectVos.add(statisticalTeacherSubjectVo);
            }
            statisticalTeacherVo.setInfo(teacherSubjectVos);
            // 统计总录题信息
            statisticalTeacherVo.setCheckNum(teacherSubjectVos.stream()
                .mapToLong(StatisticalTeacherSubjectVo::getCheckNum)
                .sum());
            statisticalTeacherVo.setEditorNum(teacherSubjectVos.stream()
                .mapToLong(StatisticalTeacherSubjectVo::getEditorNum)
                .sum());
            statisticalTeacherVo.setReviewNum(teacherSubjectVos.stream()
                .mapToLong(StatisticalTeacherSubjectVo::getReviewNum)
                .sum());
        });
        return list;
    }

    @Override
    public List<StatisticalTeacherVo> statisticalPaperTeacher(String startTime, String endTime, Long teacherId) {
        Long start = null, end = null;
        try {
            start = Objects.requireNonNull(DateUtil.parse(startTime, "yyyy-MM-dd HH:mm:ss")).getTime();
            end = Objects.requireNonNull(DateUtil.parse(endTime, "yyyy-MM-dd HH:mm:ss")).getTime();
        } catch (NullPointerException e) {
            start = 0L;
            end = 0L;
        }
        Long finalStart = start;
        Long finalEnd = end;
        // 获取老师信息分页
        List<StatisticalTeacherVo> list = PageFactory.page(() -> questionMapper
            .statisticalTeacherLeft(finalStart, finalEnd, teacherId));
        List<Long> ids = list.stream().map(StatisticalTeacherVo::getId).collect(Collectors.toList());
        // 获取试卷统计信息
        List<StatisticalTeacherInfoVo> infoVos = this.questionMapper.statisticalTeacherExamTypeRight(ids, start, end);
        list.forEach(statisticalTeacherVo -> {
            // 根据老师id筛选数据，并根据学科分组
            Map<String, List<StatisticalTeacherInfoVo>> listMap = infoVos.stream()
                .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getId()
                    .equals(statisticalTeacherVo.getId()))
                .collect(Collectors.groupingBy(StatisticalTeacherInfoVo::getExamType));
            List<StatisticalTeacherExamTypeVo> teacherExamTypeVos = new ArrayList<>();
            // 统计考试类型试卷信息
            for (Map.Entry<String, List<StatisticalTeacherInfoVo>> entry : listMap.entrySet()) {
                StatisticalTeacherExamTypeVo statisticalTeacherExamTypeVo = new StatisticalTeacherExamTypeVo();
                statisticalTeacherExamTypeVo.setExamType(entry.getKey());
                statisticalTeacherExamTypeVo.setCheckNum(entry.getValue()
                    .stream()
                    .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getType().equals("check"))
                    .mapToLong(StatisticalTeacherInfoVo::getNum)
                    .sum());
                statisticalTeacherExamTypeVo.setEditorNum(entry.getValue()
                    .stream()
                    .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getType().equals("edit"))
                    .mapToLong(StatisticalTeacherInfoVo::getNum)
                    .sum());
                statisticalTeacherExamTypeVo.setReviewNum(entry.getValue()
                    .stream()
                    .filter(statisticalTeacherInfoVo -> statisticalTeacherInfoVo.getType().equals("review"))
                    .mapToLong(StatisticalTeacherInfoVo::getNum)
                    .sum());
                teacherExamTypeVos.add(statisticalTeacherExamTypeVo);
            }
            statisticalTeacherVo.setInfo(teacherExamTypeVos);
            // 统计总试卷信息
            statisticalTeacherVo.setCheckNum(teacherExamTypeVos.stream()
                .mapToLong(StatisticalTeacherExamTypeVo::getCheckNum)
                .sum());
            statisticalTeacherVo.setEditorNum(teacherExamTypeVos.stream()
                .mapToLong(StatisticalTeacherExamTypeVo::getEditorNum)
                .sum());
            statisticalTeacherVo.setReviewNum(teacherExamTypeVos.stream()
                .mapToLong(StatisticalTeacherExamTypeVo::getReviewNum)
                .sum());
        });
        return list;
    }

    @Override
    public Map<String, Object> getQuestionUpAndDown(Long questionId, String activeName) {
        Map<String, Object> map = new HashMap<>();
        Long up = this.questionMapper.getQuestionUp(questionId, activeName, UserContextHolder.getUserId());
        Long down = this.questionMapper.getQuestionDown(questionId, activeName, UserContextHolder.getUserId());
        map.put("up", up);
        map.put("down", down);
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delQuestion(List<Long> delList, Long userId) {
        for (Long delId : delList) {
            questionEventSender.sendEvent(QuestionEvent.builder().questionId(delId).action("delete").build());
        }
        LambdaQueryWrapper<Question> delQuery = new LambdaQueryWrapper<>();
        delQuery.in(Question::getId, delList);
        delQuery.eq(Question::getOnlineStatus, 2);
        delQuery.in(Question::getEditStatus, Arrays.asList(Question.STATUS_TS, Question.STATUS_REJECTED));
        delQuery.eq(Question::getEditPeople, userId);
        return new Question().delete(delQuery);
    }

    @Override
    public List<Value> search(String title) {

        return new ArrayList<>();
    }

    @Override
    public List<SpotCheckQuestionVO> getSpotCheckQuestionList(QuerySpotCheckQuestion querySpotCheckQuestion) {
        return PageFactory.page(() -> questionMapper.getSpotCheckQuestionList(querySpotCheckQuestion));
    }

    @Override
    public SpotCheckQuestionSum getSpotCheckQuestionSum(QuerySpotCheckQuestion querySpotCheckQuestion) {
        return questionMapper.getSpotCheckQuestionSum(querySpotCheckQuestion);
    }

    @Override
    public void updateOfficialQuestion(Long id, String isOfficialQuestion, String officialQuestionSource, String year) {
        this.questionMapper.updateOfficialQuestion(id, isOfficialQuestion, officialQuestionSource, year);
    }

    @Override
    public void online(Long id, Long onlineStatus) {
        Question question = questionMapper.selectById(id);
        if (Objects.equals(question.getType(), (long)QuestionTypeEnum.MATERIAL_QUESTIONS.getCode())) {
            Long count = questionMapper.selectCount(new LambdaQueryWrapper<Question>().eq(Question::getParentId, id)
                .eq(Question::getOnlineStatus, 1));
            ValidationUtils.throwIf(count == 0, "没有子题目/或者子题目未上线");
        }
        question.setOnlineStatus(onlineStatus);
        questionMapper.updateById(question);
        questionEventSender.sendEvent(QuestionEvent.builder()
            .questionOnlineStatus(String.valueOf(question.getOnlineStatus()))
            .questionId(question.getId())
            .action("updateStatus")
            .build());
    }
}
